// ************************************************************* //
//                                                               //
//        Copyright (C) 2015 by Socionext Inc.                   //
//        All rights reserved.                                   //
//        Socionext Inc. Proprietary and Confidential.           //
//                                                               //
// ************************************************************* //
/*****************************************************************************/
/**
* @file em_host.c
* @brief eMMC host control
* @note None
* @attention None
*
*/
/*****************************************************************************/

// include -------------------------------------
// #include "common.h"
// #include "timer.h"
#include "em_int_cmn.h"
#include "em_host.h"
#include "em_host_register.h"
#include "em_dev_register.h"
#include "em_command.h"
#include "em_memorycard.h"
#include "em_dma.h"
#include "em_data.h"

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"

#include "loader.h"

#undef FILEID
#define FILEID FILE_EM_HOST

	// static functions ----------------------------
static unsigned long em_changeDeviceBusWidth(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE e_AccessMode,
	EM_BUS_WIDTH   e_BusWidth 
);

static unsigned long em_changeDeviceHSTiming(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE *e_AccessMode 
);

static unsigned long em_changeHostBusMode(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE e_AccessMode 
);

static unsigned long em_changeSDCLK(
	EM_SLOT* pSlot,
	unsigned long *pFrequencyKHz
);

static unsigned char em_checkErrorState(
	EM_SLOT* pSlot
);

static unsigned char em_checkResponseError(
	unsigned long *pResponse,
	EM_RESPONSE_TYPE e_ResponseType,
	EM_SLOT* pSlot
);

static unsigned long em_configInterrupt(
	EM_SLOT* pSlot,
	unsigned long enable
);

static unsigned long em_errorRecovery(
	EM_SLOT* pSlot,
	unsigned long ul_srs12
);

static unsigned char em_getResponse(
	EM_REQUEST_PARAM* pRequest,
	EM_SLOT* pSlot
);

static unsigned long em_resetLines(
	EM_SLOT* pSlot,
	unsigned char uc_cmd,
	unsigned char uc_dat
);

static unsigned long em_setSlotBusWidth(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE e_AccessMode,
	EM_BUS_WIDTH eBusType
);

static unsigned long em_setHostBusWidth(
	EM_SLOT* pSlot,
	EM_BUS_WIDTH eBusType
);

static unsigned long em_setSDCLK(
	EM_SLOT* pSlot,
	unsigned long *pFrequencyKHz
);

static unsigned long em_setExtCsd(
	EM_SLOT* pSlot,
	unsigned char uc_ByteNr,
	unsigned char uc_NewValue,
	unsigned char uc_Mask
);

static unsigned long em_setPower(
	EM_SLOT* pSlot, 
	unsigned long ul_Voltage
);

static unsigned long em_setTimeout(
	EM_SLOT* pSlot, 
	unsigned long ul_TimeoutVal
);

static void em_supplySDCLK(
	EM_SLOT* pSlot,
	unsigned char uc_Enable
);

static void em_transferDataBuffer(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest
);

static unsigned long em_readPHYRegister(
	EM_SLOT* pSlot,
	unsigned long ul_uis_addr,
	unsigned char *p_rdata
);

static unsigned long em_writePHYRegister(
	EM_SLOT* pSlot,
	unsigned long ul_uis_addr,
	unsigned char uc_wdata
);

static unsigned long em_execTraining(
	EM_SLOT *pSlot,
	EM_ACCESS_MODE e_AccessMode
);

static unsigned long em_calcCenterValue(
	unsigned char *p_uc_result,
	unsigned long ul_max,
	unsigned char *p_uc_center_value
);

/*****************************************************************************/
/**
 * @brief change Device bus width
 * @param[in] pSlot
 * @param[in] e_AccessMode
 * @param[in] e_BusWidth
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_FUNCTION_UNSUPP
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_changeDeviceBusWidth(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE e_AccessMode,
	EM_BUS_WIDTH   e_BusWidth 
){
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_device_status = 0;
	unsigned char uc_ArgBusWidth = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	switch(e_BusWidth)
	{
	case EM_BUS_WIDTH_1:
		uc_ArgBusWidth = EM_SWITCH_BUS_WIDTH_1;
		break;

	case EM_BUS_WIDTH_4:
		uc_ArgBusWidth = EM_SWITCH_BUS_WIDTH_4;
		break;

	case EM_BUS_WIDTH_8:
		uc_ArgBusWidth = EM_SWITCH_BUS_WIDTH_8;
		break;

	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_BusWidth );
		DEBUG_TRACE_INFO( 4, pSlot->uc_BusWidth );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER;
	}

	// DDR mask control
	switch(e_AccessMode)
	{
	case EM_ACCESS_MODE_MMC_LEGACY:
	case EM_ACCESS_MODE_HS_SDR:
		//! Legacy
		//! High Speed
		uc_ArgBusWidth &= ~EM_SWITCH_BUS_WIDTH_DDR_MASK;
		break;

	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_AccessMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER;
	}
	
	// change bus width
	ul_Ret = em_CmdMmcSwitch(pSlot, EM_EXT_CSD_BUS_WIDTH, uc_ArgBusWidth);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// send status
	ul_Ret = em_CmdSendStatus(pSlot, &ul_device_status);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, ul_device_status );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// updat the Extend CSD
	ul_Ret = em_setExtendsCSDInfo( pSlot, EM_EXT_CSD_BUS_WIDTH, uc_ArgBusWidth);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO( 4, uc_ArgBusWidth );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	return EM_OK;
}
/*****************************************************************************/
/**
 * @brief change device HS timing
 * @param[in] pSlot
 * @param[in] e_AccessMode
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_FUNCTION_UNSUPP
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_changeDeviceHSTiming(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE *e_AccessMode 
){
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_device_status = 0;
	unsigned char uc_ArgAccessMode = 0;
	unsigned char uc_MmcdeviceType = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	// access mode check
	if( *e_AccessMode != EM_ACCESS_MODE_MMC_LEGACY )
	{
		// Device Type
		uc_MmcdeviceType = (unsigned char)(GET_BYTE_FROM_BUFFER(
			pSlot->ul_ExtendsCSD,
			EM_EXT_CSD_DEVICE_TYPE));
	}

	switch(*e_AccessMode)
	{
	case EM_ACCESS_MODE_MMC_LEGACY:
		//! Legacy
		uc_ArgAccessMode = EM_HIGH_SPEED_DISABLE;
		break;

	case EM_ACCESS_MODE_HS_SDR:
		//! High Speed
		if ((uc_MmcdeviceType & EM_EXT_CSD_DEVICE_TYPE_HS_52MHZ) == 0)
		{
			//! If that is not supported, change to Legacy
			*e_AccessMode    = EM_ACCESS_MODE_MMC_LEGACY;
			uc_ArgAccessMode = EM_HIGH_SPEED_DISABLE;
		}
		else {
			uc_ArgAccessMode = EM_SWITCH_HIGH_SPEED;
		}
		break;

	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_AccessMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER;
	}

	// HS timing
	ul_Ret = em_CmdMmcSwitch(pSlot, EM_EXT_CSD_HS_TIMING, uc_ArgAccessMode);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// send status
	ul_Ret = em_CmdSendStatus(pSlot, &ul_device_status);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, ul_device_status );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// update the Extend CSD
	ul_Ret = em_setExtendsCSDInfo( pSlot, EM_EXT_CSD_HS_TIMING, uc_ArgAccessMode);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO( 4, uc_ArgAccessMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief change host bus mode
 * @param[in] pSlot
 * @param[in] e_AccessMode
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_UNSUPPORTED_OPERATION
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_changeHostBusMode(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE e_AccessMode 
){
	unsigned long ul_hrs6 = 0;
	unsigned long ul_HostMmcAccessMode = 0;
	EM_HOST* pHost = NULL;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	pHost = pSlot->pParentHost;
	if( pHost == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// access mode
	switch( e_AccessMode )
	{
	case EM_ACCESS_MODE_MMC_LEGACY:
		ul_HostMmcAccessMode = HRS6_EMMC_MODE_LEGACY;
		break;

	case EM_ACCESS_MODE_HS_SDR:
		ul_HostMmcAccessMode = HRS6_EMMC_MODE_SDR;
		break;

	case EM_ACCESS_MODE_HS_DDR:
	case EM_ACCESS_MODE_HS_200:
	case EM_ACCESS_MODE_HS_400:
		// not supported
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_AccessMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION;

	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_AccessMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER;
	}

	// HRS06:EMM - eMMC Mode select
	ul_hrs6 = EM_READ_REG( pHost->pBaseAddress->HRS.HRS6.word);
	ul_hrs6 &= ~HRS6_EMMC_MODE_MASK;
	ul_hrs6 |= ul_HostMmcAccessMode;
	EM_WRITE_REG( pHost->pBaseAddress->HRS.HRS6.word, ul_hrs6);

	return EM_OK;
}


static void wait_nsec( short waitTime )
{
	short val;
	if (waitTime >= 130) {
		val = waitTime;
	}
	else {
		val = 1;
	}
	
	wait(val);		// val : 1 = 130nsec
	
	return;
}


/*****************************************************************************/
/**
 * @brief change the SDCLK
 * @param[in] pSlot
 * @param[in,out] pFrequencyKHz
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER NULL
 * @return EM_ERR_UNSUPPORTED_OPERATION
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_changeSDCLK(
	EM_SLOT* pSlot,
	unsigned long *pFrequencyKHz
){
	unsigned long ul_srs16 = 0;
	unsigned long ul_srs11 = 0;
	unsigned long ul_srs11_sdcfs = 0;
	unsigned long ul_BaseCLKkHz = 0;
	unsigned long ul_SetFrequencyKHz  = 0;
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_Cnt = 0;
	unsigned long ul_sdcfs = 0;
	signed short ss_ns_cycle = 0;
	EM_HOST* pHost = NULL;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pFrequencyKHz == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	// Frequency
	ul_SetFrequencyKHz = *pFrequencyKHz;
	// stop SD Clock
	em_supplySDCLK(pSlot, 0);
	
	// change the Base clock(sdmclk) frequency to KHz
	ul_srs16 = EM_READ_REG( pSlot->pRegSRS->SRS16.word );
	ul_BaseCLKkHz = SRS16_GET_BASE_CLK_FREQ_MHZ( ul_srs16 ) * EM_MULTIPLE_MHZ_TO_KHZ;

	// SRS11
	ul_srs11 = EM_READ_REG( pSlot->pRegSRS->SRS11.word );
	// SDCFSL/H
	ul_sdcfs = SRS11_GET_SDCFS( ul_srs11 );
	// clear the SDCFSL/H
	ul_srs11 &= ~SRS11_SEL_FREQ_BASE_MASK;

	// wait for after stopping SDCLK
	// 5cycle@MMC_BCLK_I + Ncycle@MMC_BCLK_I(N:division value)
	ss_ns_cycle = (signed short)(1000000 / ul_BaseCLKkHz) ;
	if( ul_sdcfs == 0 ) {
		// N=0 : 1/1
		ul_sdcfs = 1;
	} else {
		// N!=0 : 1/2N
		ul_sdcfs *= 2;
	}
	wait_nsec( (short)(ss_ns_cycle * (5 + ul_sdcfs)) );

	pHost = pSlot->pParentHost;
	if( pHost == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}
	if( pHost->uc_SpecVerNum < CRS63_HOST_SPEC_VER_2_00 ) {
		// not supported
		DEBUG_TRACE_INFO_32( 0, pHost->uc_SpecVerNum );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	// 10-bit Driver Clock Mode
	if( ul_BaseCLKkHz == ul_SetFrequencyKHz )
	{
		// N=0
		ul_srs11_sdcfs = 0;
	}
	else
	{
		// Max(10bit=1023)*2
		for (ul_Cnt = 2; ul_Cnt < 2046; ul_Cnt++){
			if ((ul_BaseCLKkHz / ul_Cnt) < ul_SetFrequencyKHz)
			{
				break;
			}
			if (((ul_BaseCLKkHz / ul_Cnt) == ul_SetFrequencyKHz) && ((ul_BaseCLKkHz % ul_Cnt) == 0))
			{
				break;
			}
		}
		ul_srs11_sdcfs = ((ul_Cnt / 2) << 8);
		ul_SetFrequencyKHz = ul_BaseCLKkHz / ul_Cnt;
	}
	// SRS11:SDCLK Frequency Select/Internal Clock Enable
	ul_srs11 |= (ul_srs11_sdcfs & 0xFF00uL) | ((ul_srs11_sdcfs & 0x30000uL) >> 10)
		| SRS11_INT_CLOCK_ENABLE;

	EM_WRITE_REG( pSlot->pRegSRS->SRS11.word, ul_srs11 );


	// wait for clock stable(SRS11.ICS) = 1
	ul_Ret = em_waitForValue(
		&(pSlot->pRegSRS->SRS11.word),
		SRS11_INT_CLOCK_STABLE,
		1,
		EM_CONFIG_WAIT_VALUE_TIME );
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	*pFrequencyKHz = ul_SetFrequencyKHz ;

	return EM_OK;
}


/*****************************************************************************/
/**
 * @brief SRS12 error state
 * @param[in] pSlot
 * @return EM_ERR_UNDEFINED
 * @return EM_ERR_RESP_ERROR Response Error
 * @return EM_ERR_ADMA ADMA Error
 * @return EM_ERR_CURRENT_LIMIT_ERROR Current Limit Error
 * @return EM_ERR_DATA_END_BIT_ERROR Data End Bit Error
 * @return EM_ERR_DATA_CRC_ERROR Data CRC Error
 * @return EM_ERR_DATA_TIMEOUT_ERROR Data Timeout Error
 * @return EM_ERR_COMMAND_INDEX_ERROR Command Index Error
 * @return EM_ERR_COMMAND_END_BIT_ERROR Command End Bit Error
 * @return EM_ERR_CMD_LINE_CONFLICT
 * @return EM_ERR_COMMAND_CRC_ERROR Command CRC Error
 * @return EM_ERR_COMMAND_TIMEOUT_ERROR Command Timeout Error
 */
/*****************************************************************************/
static unsigned char em_checkErrorState(
	EM_SLOT* pSlot
){
	unsigned long ul_srs12 = 0;
	unsigned char uc_errcode = EM_ERR_UNDEFINED;
	volatile unsigned long ul_ClearIntrpt = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */
	ul_srs12 = EM_READ_REG( pSlot->pRegSRS->SRS12.word );

	do{
		if (ul_srs12 & SRS12_ADMA_ERROR)
		{
			ul_ClearIntrpt |= SRS12_ADMA_ERROR;
			uc_errcode = EM_ERR_ADMA;
			em_DMA_HandleADMAError( pSlot );
			break;
		}
		if (ul_srs12 & SRS12_AUTO_CMD_ERROR)
		{
			ul_ClearIntrpt |= SRS12_AUTO_CMD_ERROR;
			uc_errcode = EM_ERR_AUTO_CMD;
			break;
		}
		if (ul_srs12 & SRS12_CURRENT_LIMIT_ERROR)
		{
			ul_ClearIntrpt |= SRS12_CURRENT_LIMIT_ERROR;
			uc_errcode = EM_ERR_CURRENT_LIMIT_ERROR;
			break;
		}
		if (ul_srs12 & SRS12_DATA_END_BIT_ERROR)
		{
			ul_ClearIntrpt |= SRS12_DATA_END_BIT_ERROR;
			uc_errcode = EM_ERR_DATA_END_BIT_ERROR;
			break;
		}
		if (ul_srs12 & SRS12_DATA_CRC_ERROR)
		{
			ul_ClearIntrpt |= SRS12_DATA_CRC_ERROR;
			uc_errcode = EM_ERR_DATA_CRC_ERROR;
			break;
		}
		if (ul_srs12 & SRS12_DATA_TIMEOUT_ERROR)
		{
			ul_ClearIntrpt |= SRS12_DATA_TIMEOUT_ERROR;
			uc_errcode = EM_ERR_DATA_TIMEOUT_ERROR;
			break;
		}
		if (ul_srs12 & SRS12_COMMAND_INDEX_ERROR)
		{
			ul_ClearIntrpt |= SRS12_COMMAND_INDEX_ERROR;
			uc_errcode = EM_ERR_COMMAND_INDEX_ERROR;
			break;
		}
		if (ul_srs12 & SRS12_COMMAND_END_BIT_ERROR)
		{
			ul_ClearIntrpt |= SRS12_COMMAND_END_BIT_ERROR;
			uc_errcode = EM_ERR_COMMAND_END_BIT_ERROR;
			break;
		}
		if (ul_srs12 & SRS12_COMMAND_CRC_ERROR)
		{
			if (ul_srs12 & SRS12_COMMAND_TIMEOUT_ERROR)
			{
				ul_ClearIntrpt |= SRS12_COMMAND_CRC_ERROR
					| SRS12_COMMAND_TIMEOUT_ERROR;
				uc_errcode = EM_ERR_CMD_LINE_CONFLICT;
			}
			else
			{
				ul_ClearIntrpt |= SRS12_COMMAND_CRC_ERROR;
				uc_errcode = EM_ERR_COMMAND_CRC_ERROR;
			}
			break;
		}
		if (ul_srs12 & SRS12_COMMAND_TIMEOUT_ERROR)
		{
			ul_ClearIntrpt |= SRS12_COMMAND_TIMEOUT_ERROR;
			uc_errcode = EM_ERR_COMMAND_TIMEOUT_ERROR;
			break;
		}
	} while(0);

	// recover check
	if( pSlot->uc_errorRecover == EM_DISABLE )
	{
		pSlot->uc_errorRecover = EM_ENABLE;

		// error recovery
		em_errorRecovery(
			pSlot,
			ul_srs12
		);

		pSlot->uc_errorRecover = EM_DISABLE;
	}

	EM_WRITE_REG( pSlot->pRegSRS->SRS12.word , ul_ClearIntrpt );

	return uc_errcode;
}

/*****************************************************************************/
/**
 * @brief check interrupt
 * @param[in] pSlot
 * @param[in] ul_srs12
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
unsigned long em_checkInterrupt(
	EM_SLOT* pSlot,
	unsigned long ul_srs12
){
	unsigned char uc_Ret = EM_INVAL;
	EM_REQUEST_PARAM *pCurRequest = NULL;
	

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */
	do
	{
#if 0	// There is no Card Removal, Card Insertion.
		if ((ul_srs12 & SRS12_CARD_REMOVAL) || (ul_srs12 & SRS12_CARD_INSERTION))
		{
			EM_WRITE_REG( pSlot->pRegSRS->SRS12.word, (SRS12_CARD_REMOVAL | SRS12_CARD_INSERTION)) ;

			if (pSlot->pCurrentRequest)
			{
				em_DMA_handleInterrupt(pSlot, pSlot->pCurrentRequest, ul_srs12);
				pSlot->pCurrentRequest->uc_status = EM_INVAL ;
			}
			DEBUG_TRACE_INFO_32( 0, ul_srs12 );
			DEBUG_TRACE( FACT_EM_HW_ERROR, &g_sTracedata );
			break;
		}
#endif
		//! get the current request
		pCurRequest = pSlot->pCurrentRequest;
		if ( pCurRequest == NULL )
		{
			break;
		}

		// check the source of the interrupt
		if (ul_srs12 & SRS12_ERROR_INTERRUPT)
		{
			/*
			 * error interrupt
			 */
			em_DMA_handleInterrupt(pSlot, pCurRequest, ul_srs12);
			pCurRequest->uc_status = em_checkErrorState( pSlot );
			DEBUG_TRACE_INFO_32( 0, ul_srs12 );
			DEBUG_TRACE_INFO_32( 4, pSlot->pRegSRS->SRS15.word );
			DEBUG_TRACE( FACT_EM_HW_ERROR, &g_sTracedata );
			break;
		}

		if (ul_srs12 & SRS12_COMMAND_COMPLETE)
		{
			/*
			 * Command Complete
			 */
			pCurRequest->uc_InterruptCheck |= SRS12_COMMAND_COMPLETE;
			// Response
			uc_Ret = em_getResponse( pCurRequest, pSlot);
			if ( uc_Ret != EM_OK )
			{
				pCurRequest->uc_status = uc_Ret;
				break;
			}
			// if request doesn't need to transfer data
			// the transaction is finish
			if ((pCurRequest->sRequestFlags.uc_dataPresent == EM_DISABLE)
				&& (pCurRequest->sRequestFlags.e_responseType != EM_RESPONSE_R1B))
			{
				pCurRequest->uc_status = EM_OK;
				pSlot->pCurrentRequest = NULL;
				break;
			}

			if ((pCurRequest->sRequestFlags.e_responseType == EM_RESPONSE_R1B))
			{
				// In the case of R1B, check DAT received
				if ((pCurRequest->uc_InterruptCheck & (SRS12_COMMAND_COMPLETE | SRS12_TRANSFER_COMPLETE)) ==
					(SRS12_COMMAND_COMPLETE | SRS12_TRANSFER_COMPLETE))
				{
					pCurRequest->uc_status = EM_OK;
					pSlot->pCurrentRequest = NULL;
					break;
				}
			}
		}

		if (ul_srs12 & SRS12_BUFFER_READ_READY)
		{
			/*
			 * buffer read ready
			 */
			if ( pCurRequest->pBufferPos == NULL )
			{
				DEBUG_TRACE_INFO_32( 0, ul_srs12 );
				DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
				break;
			}
			if ( pCurRequest->sRequestFlags.e_dataTransferDirection == EM_TRANSFER_READ)
			{
				// buffer read
				em_transferDataBuffer( pSlot, pSlot->pCurrentRequest );
			}
		}

		if (ul_srs12 & SRS12_BUFFER_WRITE_READY)
		{
			/*
			 * buffer write ready
			 */
			if ( pCurRequest->pBufferPos == NULL )
			{
				DEBUG_TRACE_INFO_32( 0, ul_srs12 );
				DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
				break;
			}
			if ( pCurRequest->sRequestFlags.e_dataTransferDirection == EM_TRANSFER_WRITE)
			{
				// buffer write
				em_transferDataBuffer( pSlot, pSlot->pCurrentRequest );
			}
		}

		if (ul_srs12 & SRS12_TRANSFER_COMPLETE)
		{
			/*
			 * transfer complete
			 */
			em_DMA_handleInterrupt(pSlot, pSlot->pCurrentRequest, ul_srs12);

			pCurRequest->uc_InterruptCheck |= SRS12_TRANSFER_COMPLETE;

			if (pCurRequest->sRequestFlags.e_responseType == EM_RESPONSE_R1B)
			{
				// In the case of R1B, check Command Complete
				if ((pCurRequest->uc_InterruptCheck & (SRS12_COMMAND_COMPLETE | SRS12_TRANSFER_COMPLETE)) ==
					(SRS12_COMMAND_COMPLETE | SRS12_TRANSFER_COMPLETE))
				{
					pCurRequest->uc_status = EM_OK;
					pSlot->pCurrentRequest = NULL;
				}
				break;
			}

			pCurRequest->uc_status = EM_OK;
			pSlot->pCurrentRequest = NULL;
		}
	} while(0);
	
	return EM_OK;
}


/*****************************************************************************/
/**
 * @brief check response error
 * @param[in] pResponse Response #1
 * @param[in] e_ResponseType Response Type
 * @param[in] pSlot
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEVICE_STATUS_ERROR
 */
/*****************************************************************************/
static unsigned char em_checkResponseError(
	unsigned long *pResponse,
	EM_RESPONSE_TYPE e_ResponseType,
	EM_SLOT* pSlot
){
	/**
	 * parameter check
	 */
	if( pResponse == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	//! Response Type
	switch (e_ResponseType)
	{
	case EM_RESPONSE_R1:
		/*
		 * Device State Type"E" check
		 */
		if ((pResponse[0] & DEVICE_STATUS_ALL_ERRORS_MASK) != 0 )
		{
			DEBUG_TRACE_INFO_32( 0, pResponse[0] );
			DEBUG_TRACE_INFO_32( 4, (unsigned long)e_ResponseType );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_ERR_DEVICE_STATUS_ERROR;
		}
		break;

	default:
		break;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set the access mode
 * @param[in] pSlot
 * @param[in] e_AccessMode
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
unsigned long em_configAccessMode(
	EM_SLOT *pSlot,
	EM_ACCESS_MODE *e_AccessMode
){
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_FrequencyKHz = 0;
	
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	if ( pSlot->pDevice == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// select card
	ul_Ret = em_CmdSelectCard(pSlot, pSlot->pDevice->us_RCA, EM_DISABLE);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Device HS Timing
	ul_Ret = em_changeDeviceHSTiming(pSlot, e_AccessMode);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Host Bus mode
	ul_Ret = em_changeHostBusMode(pSlot, *e_AccessMode);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Frequency
	if( *e_AccessMode == EM_ACCESS_MODE_MMC_LEGACY )
	{
		// 26MHz
		ul_FrequencyKHz = 25000 ;
	} else {
		// 52MHz
		ul_FrequencyKHz = 50000 ;
	}

	// SDCLK
	ul_Ret = em_setSDCLK(pSlot, &ul_FrequencyKHz);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, ul_FrequencyKHz );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// training(>400KHz)
	if( ul_FrequencyKHz > 400 )
	{
		ul_Ret = em_execTraining(
			pSlot,
			*e_AccessMode
		);
		if ( ul_Ret != EM_OK ) 
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE_INFO_32( 4, ul_FrequencyKHz );
			DEBUG_TRACE_INFO_32( 8, (unsigned long)*e_AccessMode );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
	}

	pSlot->e_AccessMode = *e_AccessMode;
	
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief config interrupt
 * @param[in] pSlot
 * @param[in] enable
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_configInterrupt(
	EM_SLOT* pSlot,
	unsigned long enable
){
	unsigned long ul_srs14 = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	if ( enable != EM_DISABLE )
	{
		ul_srs14 = SRS14_ADMA_ERROR_SIG_EN
			| SRS14_AUTO_CMD12_ERR_SIG_EN
			| SRS14_CURRENT_LIMIT_ERR_SIG_EN
			| SRS14_DATA_END_BIT_ERR_SIG_EN
			| SRS14_DATA_CRC_ERR_SIG_EN
			| SRS14_DATA_TIMEOUT_ERR_SIG_EN
			| SRS14_COMMAND_INDEX_ERR_SIG_EN
			| SRS14_COMMAND_END_BIT_ERR_SIG_EN
			| SRS14_COMMAND_CRC_ERR_SIG_EN
			| SRS14_COMMAND_TIMEOUT_ERR_SIG_EN
			| SRS14_BUFFER_READ_READY_SIG_EN
			| SRS14_TRANSFER_COMPLETE_SIG_EN
			| SRS14_COMMAND_COMPLETE_SIG_EN;
	}

	EM_WRITE_REG( pSlot->pRegSRS->SRS14.word, ul_srs14 );

	return EM_OK;
}


/*****************************************************************************/
/**
 * @brief device attach
 * @param[in] pSlot
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
unsigned long em_deviceAttach(
	EM_SLOT* pSlot
){
	EM_HOST* pHost = NULL;
	const unsigned long ul_data_timeout_ms = EM_DATA_TIMEOUT_COUNTER_VALUE; 
	unsigned long ul_FrequencyKHz = 400;
	unsigned long ul_CurContVoltage = SRS10_SET_1_8V_BUS_VOLTAGE;
	unsigned long ul_CardVoltage = EM_REG_OCR_1_7_1_95;
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_srs13 = 0;
	unsigned long ul_srs16 = 0;
	unsigned long ul_timeout_interval = 0;
	unsigned long ul_tcf_khz = 0;
	unsigned long ul_Cnt = 0;
	unsigned char uc_AccessMode = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	// soft reset
	ul_Ret = em_resetSlot(
		pSlot
	);
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// wait 1ms
	wait(7693);		// 7693 = 7693 * 130ns = 1000090ns = 1ms

	//! SRS16.Base Clock Frequency For SD Clock
	ul_srs16 = EM_READ_REG( pSlot->pRegSRS->SRS16.word ) ;
	ul_tcf_khz = SRS16_GET_BASE_CLK_FREQ_MHZ( ul_srs16 ) * EM_MULTIPLE_MHZ_TO_KHZ;
	if (ul_tcf_khz == 0)
	{
		DEBUG_TRACE_INFO_32( 0, ul_srs16 );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_BASE_CLOCK_IS_ZERO;
	}

	//! SRS11.Data Timeout Counter Value calculate
	ul_timeout_interval = EM_DTCV_TIMEOUT_INTERVAL_START;	   /* 2^(12+2) */;
	for(ul_Cnt = 0; ul_Cnt < 15; ul_Cnt++)
	{
		ul_timeout_interval *= 2;
		if (ul_data_timeout_ms < (ul_timeout_interval / ul_tcf_khz))
		{
			break;
		}
	}
	ul_timeout_interval = ul_Cnt << 16;
	em_setTimeout( pSlot, ul_timeout_interval );

	//! set power
	ul_Ret = em_setPower(pSlot, ul_CurContVoltage);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	//! clock frequency ( 400kHz )
	ul_Ret = em_setSDCLK(pSlot, &ul_FrequencyKHz);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// SRS12, 13, 14
	ul_srs13 = SRS13_ADMA_ERROR_STAT_EN
		| SRS13_AUTO_CMD12_ERR_STAT_EN
		| SRS13_CURRENT_LIMIT_ERR_STAT_EN
		| SRS13_DATA_END_BIT_ERR_STAT_EN
		| SRS13_DATA_CRC_ERR_STAT_EN
		| SRS13_DATA_TIMEOUT_ERR_STAT_EN
		| SRS13_COMMAND_INDEX_ERR_STAT_EN
		| SRS13_COMMAND_END_BIT_ERR_STAT_EN
		| SRS13_COMMAND_CRC_ERR_STAT_EN
		| SRS13_COMMAND_TIMEOUT_ERR_STAT_EN
		| SRS13_BUFFER_READ_READY_STAT_EN
		| SRS13_TRANSFER_COMPLETE_STAT_EN
		| SRS13_COMMAND_COMPLETE_STAT_EN ;

	EM_WRITE_REG( pSlot->pRegSRS->SRS12.word , 0xFFFFFFFFuL ) ;
	EM_WRITE_REG( pSlot->pRegSRS->SRS13.word , ul_srs13 ) ;

	// config interrupt
	ul_Ret = em_configInterrupt( pSlot, EM_DISABLE ) ;
	if( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Reset for Device
	pHost = pSlot->pParentHost;
	if( pHost == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}
	EM_WRITE_REG( pHost->pBaseAddress->HRS.HRS41.word, HRS41_RSTNC);

	// wait 200us
	wait(1539);		// 1539 = 1539 * 130ns = 200070ns = 200.07us

	// CMD0(Arg=0) card reset
	ul_Ret = em_CmdResetCard(pSlot);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// CMD1(voltage setting)
	ul_Ret = em_CmdSetMemCardVoltage(
		pSlot,
		ul_CardVoltage,
		&uc_AccessMode);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	if ( pSlot->pDevice == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// device capacity
	if ( uc_AccessMode == EM_ENABLE )
	{
		pSlot->pDevice->uc_DeviceCapacity = EM_CAPACITY_HIGH;
	}
	else
	{
		pSlot->pDevice->uc_DeviceCapacity = EM_CAPACITY_NORMAL;
	}

	// CMD2(CID)
	ul_Ret = em_CmdSendCID( pSlot );
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// CMD3(RCA)
	ul_Ret = em_CmdSetRCA( pSlot );
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	pSlot->uc_CardInserted = EM_ENABLE;
	pSlot->uc_NeedAttach = EM_DISABLE;

	// CSD
	ul_Ret = em_memoryCard_GetCSD( pSlot->pDevice );
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Host Bus Width(fixed 8)
	ul_Ret = em_setSlotBusWidth(
		pSlot,
		EM_ACCESS_MODE_MMC_LEGACY,
		EM_BUS_WIDTH_8 );
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO( 4, pSlot->uc_BusWidth );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	// memory card initialize
	ul_Ret = em_memoryCard_Initialize(
		pSlot
	);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// AccessMode,bus width:8
	ul_Ret = em_configAccessMode(
		pSlot,
		(EM_ACCESS_MODE*)&e_gAccessMode
	);
	if( ul_Ret != EM_OK ) {
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief error recovery
 * @param[in] pSlot
 * @param[in] ul_srs12
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_errorRecovery(
	EM_SLOT* pSlot,
	unsigned long ul_srs12
){
	unsigned long ul_ret = 0;
	unsigned long ul_srs14_temp = 0;
	unsigned long ul_srs14 = 0;
	unsigned char uc_cmdReset = EM_DISABLE;
	unsigned char uc_datReset = EM_DISABLE;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	ul_srs14 = EM_READ_REG( pSlot->pRegSRS->SRS14.word ) ;
	// SRS14 disable
	ul_srs14_temp = ul_srs14 & (~(SRS14_COMMAND_COMPLETE_SIG_EN
						 | SRS14_TRANSFER_COMPLETE_SIG_EN
						 | SRS14_COMMAND_TIMEOUT_ERR_SIG_EN
						 | SRS14_COMMAND_CRC_ERR_SIG_EN
						 | SRS14_COMMAND_END_BIT_ERR_SIG_EN
						 | SRS14_COMMAND_INDEX_ERR_SIG_EN
						 | SRS14_DATA_TIMEOUT_ERR_SIG_EN
						 | SRS14_DATA_CRC_ERR_SIG_EN
						 | SRS14_DATA_END_BIT_ERR_SIG_EN
						 | SRS14_CURRENT_LIMIT_ERR_SIG_EN
						 | SRS14_AUTO_CMD12_ERR_SIG_EN));
	EM_WRITE_REG( pSlot->pRegSRS->SRS14.word , ul_srs14_temp );


	// check the CMD Line error
	if ( ul_srs12 &
			(SRS12_COMMAND_TIMEOUT_ERROR | SRS12_COMMAND_CRC_ERROR |
			 SRS12_COMMAND_END_BIT_ERROR | SRS12_COMMAND_INDEX_ERROR ))
	{
		uc_cmdReset = EM_ENABLE ;
	}

	// check the DAT Line error
	if ( ul_srs12 & 
		   (SRS12_DATA_END_BIT_ERROR | SRS12_DATA_CRC_ERROR | SRS12_DATA_TIMEOUT_ERROR))
	{
		uc_datReset = EM_ENABLE ;
	}

	if( (uc_cmdReset == EM_ENABLE ) || (uc_datReset == EM_ENABLE ))
	{
		// CMD line,DAT line Reset
		ul_ret = em_resetLines(
			pSlot,
			EM_ENABLE,
			EM_ENABLE
		);
		if( ul_ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			// Continue the process
		}

		// wait 1ms
		wait(7693);		// 7693 = 7693 * 130ns = 1000090ns = 1ms
	}

	// Error Interrupt clear
	EM_WRITE_REG( pSlot->pRegSRS->SRS12.word , ul_srs12 );

	// SRS14 reinstate
	EM_WRITE_REG( pSlot->pRegSRS->SRS14.word , ul_srs14 );

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief get response
 * @param[in] pRequest
 * @param[in] pSlot
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
static unsigned char em_getResponse(
	EM_REQUEST_PARAM* pRequest,
	EM_SLOT* pSlot
){
	long iCnt = 0;
	unsigned char uc_Ret = EM_INVAL;
	/**
	 * parameter check
	 */
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	if ( pRequest->uc_respSize == 0 )
	{
		return EM_OK;
	}

	// clear response(except for response[0])
	pRequest->ul_responseTab[1] = 0;
	pRequest->ul_responseTab[2] = 0;
	pRequest->ul_responseTab[3] = 0;

	for (iCnt = 0; (iCnt < (pRequest->uc_respSize / 4)) && (iCnt < EM_RESP_TAB_SIZE); iCnt++)
	{
		pRequest->ul_responseTab[ iCnt ] = EM_READ_REG(	 pSlot->pRegSRS->u_4_7.Response[ iCnt ] ) ;
	}

	pRequest->p_response = &(pRequest->ul_responseTab[0]);

	if ( pSlot->pDevice == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	//! RCA check
	if (pSlot->pDevice->us_RCA != 0)
	{
		// check the response error
		uc_Ret = em_checkResponseError(
			pRequest->p_response,
			pRequest->sRequestFlags.e_responseType,
			pSlot);
		if ( uc_Ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, uc_Ret );
			DEBUG_TRACE_INFO_32( 4, (unsigned long)pSlot->pDevice->us_RCA );
			DEBUG_TRACE( FACT_EM_HW_ERROR, &g_sTracedata );
			return EM_INVAL ;
		}
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief initialze slot
 * @param[in] pSlot
 * @param[in] pHost
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
unsigned long em_initializeSlot(
	EM_SLOT* pSlot,
	EM_HOST* pHost
){

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pHost == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	pSlot->uc_CardInserted = EM_DISABLE;
	pSlot->uc_NeedAttach = EM_ENABLE;
	pSlot->pParentHost = pHost;
	pSlot->eDmaMode = EM_DMA_AUTO_MODE;
	pSlot->uc_errorRecover = EM_DISABLE;

	// device information
	pSlot->pDevice = &(pSlot->sDevices[0]);
	pSlot->pDevice->pSlot = pSlot ;
	pSlot->pDevice->us_RCA = 0 ;
	pSlot->pDevice->uc_DeviceCapacity = 0 ;
	pSlot->pDevice->uc_SpecVersNumb = 0 ;
	pSlot->pDevice->uc_SupportedBusWidths = 0 ;
	pSlot->pDevice->uc_IsSelected = 0 ;
	// memory card information
	pSlot->pDevice->sCardInfo.ul_DeviceSizeMB = 0;
	pSlot->pDevice->sCardInfo.ul_max_frequency = 0;
	pSlot->pDevice->sCardInfo.us_BlockSize = 0;
	// command request information
	pSlot->pCurrentRequest = NULL;
	// SRS1/SDMA Buffer Boundary area
	pSlot->ul_DMABufferBoundary = 0;

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief read CSD
 * @param[in] pSlot
 * @param[in] pBuffer
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
unsigned long em_readCSD_Proc(
	EM_SLOT* pSlot,
	unsigned long *pBuffer
){
	unsigned long ul_Ret = EM_INVAL;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pBuffer == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	// select card(Standby state)
	ul_Ret = em_CmdSelectCard(pSlot, 0, EM_DISABLE);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// CSD
	ul_Ret = em_CmdReadCSD(pSlot, pBuffer);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief recover register
 * @param[in] pSlot
 * @return None
 */
/*****************************************************************************/
void em_recoverRegister(
	EM_SLOT *pSlot
){
	unsigned long ul_Ret = EM_INVAL;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return	;
	} 

	/**
	 * Process start
	 */

	// CMD0
	ul_Ret = em_CmdResetCard(pSlot);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
	}

}

/*****************************************************************************/
/**
 * @brief reset CMD,DAT line
 * @param[in] pSlot
 * @param[in] uc_cmd
 * @param[in] uc_dat
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_resetLines(
	EM_SLOT* pSlot,
	unsigned char uc_cmd,
	unsigned char uc_dat
){
	unsigned long ul_srs11 = 0;
	unsigned long ul_mask = 0;
	unsigned long ul_Ret = EM_INVAL;
	long iCnt = 0;
	
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */

	if (uc_cmd)
	{
		// CMD line
		ul_mask |= SRS11_SOFT_RESET_CMD_LINE;
	}
	if (uc_dat)
	{
		// DAT line
		ul_mask |= SRS11_SOFT_RESET_DAT_LINE;
	}

	if( ul_mask == 0 )
	{
		return EM_OK;
	}

	for (iCnt = 0; iCnt < EM_CONFIG_RESET_COUNT; iCnt++)
	{
		ul_srs11 = EM_READ_REG( pSlot->pRegSRS->SRS11.word);
		ul_srs11 |= ul_mask;
		EM_WRITE_REG( pSlot->pRegSRS->SRS11.word, ul_srs11);

		ul_Ret = em_waitForValue(
			&(pSlot->pRegSRS->SRS11.word),
			ul_mask,
			0,
			EM_CONFIG_WAIT_VALUE_TIME );

		if (ul_Ret != EM_OK)
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
	}
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief reset slog
 * @param[in] pSlot
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
unsigned long em_resetSlot(
	EM_SLOT* pSlot
){
	unsigned long ul_Cnt = 0;
	unsigned long ul_Ret = 0;
	unsigned long ul_srs11 = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */
	for (ul_Cnt = 0; ul_Cnt < EM_CONFIG_RESET_COUNT; ul_Cnt++)
	{
		//! reset slot
		ul_srs11 = EM_READ_REG( pSlot->pRegSRS->SRS11.word) ;
		//! clear SRDAT, SRCMD
		ul_srs11 &= ~(SRS11_SOFT_RESET_DAT_LINE | SRS11_SOFT_RESET_CMD_LINE);
		//! set SRFA
		ul_srs11 |= SRS11_SOFT_RESET_FOR_ALL;
		EM_WRITE_REG( pSlot->pRegSRS->SRS11.word, ul_srs11 ) ;

		//! wait status(SRS11.SRFA = 0)
		ul_Ret = em_waitForValue(
			&(pSlot->pRegSRS->SRS11.word),
			SRS11_SOFT_RESET_FOR_ALL,
			0,
			EM_CONFIG_WAIT_VALUE_TIME );
		if ( ul_Ret != EM_OK ){
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
	}
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief Extends CSD
 * @param[in] pSlot
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
unsigned long em_memExtCsdInfo(
	EM_SLOT* pSlot
){
	unsigned char *p_read_buffer = NULL;
	unsigned long ul_Ret = EM_INVAL;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	if ( pSlot->pDevice == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// card select
	ul_Ret = em_CmdSelectCard(pSlot, pSlot->pDevice->us_RCA, EM_DISABLE);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// set block length
	ul_Ret = em_CmdSetBlockLen(pSlot, EXTCSD_SIZE, EM_DISABLE );
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// M0<->A7 address conversion (DMA transfer only)
	p_read_buffer = (unsigned char*)pSlot->ul_ExtendsCSD;
	// Extends CSD
	ul_Ret = em_CmdReadExCSD(pSlot, p_read_buffer );
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief Extends CSD
 * @param[in] pSlot
 * @param[in] uc_ByteNr
 * @param[in] uc_NewValue
 * @param[in] uc_Mask
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_SETTING_EXT_CSD_FAILED
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
static unsigned long em_setExtCsd(
	EM_SLOT* pSlot,
	unsigned char uc_ByteNr,
	unsigned char uc_NewValue,
	unsigned char uc_Mask
){
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_device_status = 0;
	unsigned char uc_ByteNew = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	if ( pSlot->pDevice == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// get register value
	uc_ByteNew = (unsigned char)(GET_BYTE_FROM_BUFFER(pSlot->ul_ExtendsCSD, uc_ByteNr));
	uc_ByteNew &= ~uc_Mask;
	uc_ByteNew |= uc_NewValue;

	// update the Extends CSD
	ul_Ret = em_CmdMmcSwitch(pSlot, uc_ByteNr, uc_ByteNew);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// send status
	ul_Ret = em_CmdSendStatus(pSlot, &ul_device_status);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, ul_device_status );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// update the Extend CSD
	ul_Ret = em_setExtendsCSDInfo( pSlot, uc_ByteNr, uc_ByteNew);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO( 4, uc_ByteNr );
		DEBUG_TRACE_INFO( 5, uc_ByteNew );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief Partition Access
 * @param[in] pSlot
 * @param[in] e_Partition
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_UNSUPPORTED_OPERATION
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
unsigned long em_setPartAccess(
	EM_SLOT* pSlot,
	EM_PARTITION_ACCESS e_Partition
){
	unsigned long ul_Ret = EM_INVAL;
	unsigned char uc_PartConfig = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */

	switch(e_Partition)
	{
	case EM_ACCCESS_BOOT_NONE:
		uc_PartConfig = EM_EXT_CSD_BOOT_PART_CONFIG_ACCESS_NONE;
		break;

	case EM_ACCCESS_BOOT_1:
		uc_PartConfig = EM_EXT_CSD_BOOT_PART_CONFIG_ACCESS_BOOT_1;
		break;
		
	case EM_ACCCESS_BOOT_2:
	case EM_ACCCESS_BOOT_GENERAL_PURP_1:
	case EM_ACCCESS_BOOT_GENERAL_PURP_2:
	case EM_ACCCESS_BOOT_GENERAL_PURP_3:
	case EM_ACCCESS_BOOT_GENERAL_PURP_4:
	case EM_ACCCESS_BOOT_RPMB:
		// not supported
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_Partition );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION;
		
	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_Partition );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER;
	}

	// update the Extends CSD
	ul_Ret = em_setExtCsd(
		pSlot,
		EM_EXT_CSD_BOOT_PART_CONFIG,
		uc_PartConfig,
		EM_EXT_CSD_BOOT_PART_CONFIG_ACCESS_MASK);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set power
 * @param[in] pSlot
 * @param[in] ul_Voltage
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_UNSUPPORTED_OPERATION
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_setPower(
	EM_SLOT* pSlot, 
	unsigned long ul_Voltage
){
	unsigned long ul_srs10 = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */

	//! SRS10/SD Bus Power for VDD1 is Disable
	ul_srs10 = EM_READ_REG( pSlot->pRegSRS->SRS10.word );
	ul_srs10 &= ~SRS10_SD_BUS_POWER;
	EM_WRITE_REG( pSlot->pRegSRS->SRS10.word, ul_srs10 );

	//! clear SRS10/SD Bus Voltage Select[9:11]
	ul_srs10 &= ~SRS10_BUS_VOLTAGE_MASK;

	//! Voltage
	switch ( ul_Voltage )
	{
	case 0:
		// off
		return EM_OK;
		
	case SRS10_SET_3_3V_BUS_VOLTAGE:
	case SRS10_SET_3_0V_BUS_VOLTAGE:
		//! 3.0V,3.3V not supported
		DEBUG_TRACE_INFO_32( 0, ul_Voltage );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION;

	case SRS10_SET_1_8V_BUS_VOLTAGE:
		// set new voltage value
		ul_srs10 |= SRS10_SET_1_8V_BUS_VOLTAGE | SRS10_SD_BUS_POWER;
		break;

	default:
		DEBUG_TRACE_INFO_32( 0, ul_Voltage );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER;
	}

	EM_WRITE_REG( pSlot->pRegSRS->SRS10.word, ul_srs10 );

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set SDCLK
 * @param[in] pSlot
 * @param[in] pFrequencyKHz
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_setSDCLK(
	EM_SLOT* pSlot,
	unsigned long *pFrequencyKHz
){
	unsigned long ul_Ret = EM_INVAL;
	unsigned long ul_Start = 0;
#if 1
	TickType_t sSysTime = 0;
#else
	T_SYSTIME sSysTime = {0} ;
#endif
	unsigned char uc_rdata = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( pFrequencyKHz == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	
	// change SDCLK
	ul_Ret = em_changeSDCLK(pSlot, pFrequencyKHz);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// DLL Lock Value/DLL Lock check
	// get time
#if 1
	sSysTime = xTaskGetTickCount();
	ul_Start = sSysTime;
#else
	get_tim_rom( &sSysTime );
	ul_Start = sSysTime.ltime ;
#endif
	// check DLL Lock Value/DLL Lock = 1
	while ((uc_rdata & 0x80) == 0)
	{
		ul_Ret = em_readPHYRegister(
			pSlot,
			HRS4_UIS_ADDR_DLL_LOCK_VALUE,
			&uc_rdata
		);
		if ( ul_Ret != EM_OK ) 
		{
			DEBUG_TRACE_INFO_32( 0, ul_Ret );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
#if 1
		sSysTime = xTaskGetTickCount();
		if ((sSysTime - ul_Start) > EM_CONFIG_WAIT_VALUE_TIME)
#else
		get_tim_rom( &sSysTime );
		if ((sSysTime.ltime - ul_Start) > EM_CONFIG_WAIT_VALUE_TIME)
#endif
		{
			// timeout
			DEBUG_TRACE_INFO_32( 0, ul_Start );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_ERR_TIMEOUT;
		}
	}

	// supply SDCLK
	em_supplySDCLK(pSlot, EM_ENABLE);

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set slot bus width
 * @param[in] pSlot
 * @param[in] e_AccessMode
 * @param[in] eBusType
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_setSlotBusWidth(
	EM_SLOT* pSlot,
	EM_ACCESS_MODE e_AccessMode,
	EM_BUS_WIDTH eBusType
){
	unsigned long ul_Ret = EM_INVAL;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	if ( pSlot->pDevice == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// select card
	ul_Ret = em_CmdSelectCard(pSlot, pSlot->pDevice->us_RCA, EM_DISABLE);
	if ( ul_Ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Host Bus Width(fixed 8(SDR))
	ul_Ret = em_setHostBusWidth(pSlot, eBusType );
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, (unsigned long)eBusType );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// Device Bus Width
	ul_Ret = em_changeDeviceBusWidth(pSlot, e_AccessMode, eBusType);
	if ( ul_Ret != EM_OK ) 
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE_INFO_32( 4, (unsigned long)e_AccessMode );
		DEBUG_TRACE_INFO_32( 8, (unsigned long)eBusType );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set host bus width
 * @param[in] pSlot
 * @param[in] eBusType
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_setHostBusWidth(
	EM_SLOT* pSlot,
	EM_BUS_WIDTH eBusType
){
	unsigned long ul_srs10 = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	// SRS10
	ul_srs10 = EM_READ_REG( pSlot->pRegSRS->SRS10.word);

	switch(eBusType)
	{
	case EM_BUS_WIDTH_1:
		ul_srs10 &= ~SRS10_DATA_WIDTH_4BIT;
		ul_srs10 &= ~SRS10_EXTENDED_DATA_TRANSFER_WIDTH;
		break;

	case EM_BUS_WIDTH_4:
		ul_srs10 |= SRS10_DATA_WIDTH_4BIT;
		ul_srs10 &= ~SRS10_EXTENDED_DATA_TRANSFER_WIDTH;
		break;

	case EM_BUS_WIDTH_8:
		ul_srs10 |= SRS10_EXTENDED_DATA_TRANSFER_WIDTH;
		break;

	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)eBusType );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_INVALID_PARAMETER ;
	}
	
	EM_WRITE_REG( pSlot->pRegSRS->SRS10.word, ul_srs10);
	pSlot->uc_BusWidth = (unsigned char)eBusType ;

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set timeout
 * @param[in] pSlot
 * @param[in] ul_TimeoutVal Data Timeout Counter Value
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_INVALID_PARAMETER
 */
/*****************************************************************************/
static unsigned long em_setTimeout(
	EM_SLOT* pSlot, 
	unsigned long ul_TimeoutVal
){
	unsigned long ul_srs11 = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}
	if(!(ul_TimeoutVal & SRS11_TIMEOUT_MASK)){
		DEBUG_TRACE_INFO_32( 0, ul_TimeoutVal );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		// Continue the process
	}

	/**
	 * Process start
	 */

	// SRS11
	ul_srs11 = EM_READ_REG( pSlot->pRegSRS->SRS11.word );
	// SRS11:DTCV - Data Timeout Counter Value
	ul_srs11 &= ~SRS11_TIMEOUT_MASK;
	ul_srs11 |= ul_TimeoutVal;
	EM_WRITE_REG( pSlot->pRegSRS->SRS11.word, ul_srs11 );

	return EM_OK;
}



/*****************************************************************************/
/**
 * @brief supply SDCLK
 * @param[in] pSlot
 * @param[in] uc_Enable
 * @return None
 */
/*****************************************************************************/
static void em_supplySDCLK(
	EM_SLOT* pSlot,
	unsigned char uc_Enable)
{
	unsigned long ul_srs11 = 0;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return	;
	} ;

	/**
	 * Process start
	 */
	
	// SRS11
	ul_srs11 = EM_READ_REG( pSlot->pRegSRS->SRS11.word );

	// SRS11:SDCE - SD Clock Enable
	if ( uc_Enable == EM_ENABLE )
	{
		ul_srs11 |= SRS11_SD_CLOCK_ENABLE;
	} else {
		ul_srs11 &= ~SRS11_SD_CLOCK_ENABLE;
	}
	EM_WRITE_REG( pSlot->pRegSRS->SRS11.word, ul_srs11 );
}

/*****************************************************************************/
/**
 * @brief transfer data buffer
 * @param[in] pSlot
 * @param[in] pRequest
 * @return EM_OK Success
 */
/*****************************************************************************/
static void em_transferDataBuffer(
	EM_SLOT* pSlot,
	EM_REQUEST_PARAM* pRequest
){
	unsigned long ul_ToCopy = 0;
	/**
	 * parameter check
	 */
	if( pRequest == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return ;
	}
	if( pSlot == NULL ) {
		pRequest->uc_status = EM_ERR_INVALID_PARAMETER;
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return ;
	}

	/**
	 * Process start
	 */
	
	if (pRequest->ul_dataRemaining & 3)
	{
		// not 4byye align
		pRequest->uc_status = EM_ERR_INVALID_PARAMETER;
		DEBUG_TRACE_INFO_32( 0, pRequest->ul_dataRemaining );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return;
	}
	ul_ToCopy = pRequest->ul_dataRemaining;
	pRequest->ul_dataRemaining = 0;

	if (pRequest->sRequestFlags.e_dataTransferDirection == EM_TRANSFER_READ)
	{
		while( ul_ToCopy > 0 )
		{
			// get 4byte from Data Buffer
			*((unsigned long*)pRequest->pBufferPos) = EM_READ_REG( pSlot->pRegSRS->SRS8.word );
			pRequest->pBufferPos = ((unsigned long*)pRequest->pBufferPos) + 1;
			ul_ToCopy -= 4;
		}
	} else {
		while( ul_ToCopy > 0 )
		{
			// set 4byte from Data Buffer
			EM_WRITE_REG( pSlot->pRegSRS->SRS8.word, *((unsigned long*)pRequest->pBufferPos) );
			pRequest->pBufferPos = ((unsigned long*)pRequest->pBufferPos) + 1;
			ul_ToCopy -= 4;
		}
	}
	
}

/*****************************************************************************/
/**
 * @brief wait for value
 * @param[in] pAddress
 * @param[in] ul_Mask
 * @param[in] uc_set 0..wait for clear / other..wait for set
 * @param[in] ul_timeout_ms
 * @return EM_OK
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_TIMEOUT
 */
/*****************************************************************************/
unsigned long em_waitForValue(
	volatile unsigned long *pAddress,
	unsigned long ul_Mask,
	unsigned char uc_set,
	unsigned long ul_timeout_ms
){
	unsigned long ul_Start = 0;
#if 1
	TickType_t sSysTime = 0;
#else
	T_SYSTIME sSysTime = {0} ;
#endif
	
	/**
	 * parameter check
	 */
	if( pAddress == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */
#if 1
	sSysTime = xTaskGetTickCount();
	ul_Start = sSysTime;
#else
	get_tim_rom( &sSysTime );
	ul_Start = sSysTime.ltime ;
#endif
	
	if (uc_set == 0)
	{
		// wait for clear
		while (((*pAddress & ul_Mask) != 0))
		{
#if 1
			sSysTime = xTaskGetTickCount();
			if ((sSysTime - ul_Start) > ul_timeout_ms)
#else
			get_tim_rom( &sSysTime );
			if ((sSysTime.ltime - ul_Start) > ul_timeout_ms)
#endif
			{
				// timeout
				DEBUG_TRACE_INFO_32( 0, ul_Start );
				DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
				return EM_ERR_TIMEOUT;
			}
		}
	} else {
		// wait for set
		while (((*pAddress & ul_Mask) == 0))
		{
#if 1
			sSysTime = xTaskGetTickCount();
			if ((sSysTime - ul_Start) > ul_timeout_ms)
#else
			get_tim_rom( &sSysTime );
			if ((sSysTime.ltime - ul_Start) > ul_timeout_ms)
#endif
			{
				// timeout
				DEBUG_TRACE_INFO_32( 0, ul_Start );
				DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
				return EM_ERR_TIMEOUT;
			}
		}
	}
	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief UHS-I PHY Register
 * @param[in] pSlot
 * @param[in] ul_uis_addr
 * @param[out] p_rdata
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
static unsigned long em_readPHYRegister(
	EM_SLOT* pSlot,
	unsigned long ul_uis_addr,
	unsigned char *p_rdata
){
	EM_HOST* pHost = NULL;
	unsigned long ul_hrs04 = 0;
	unsigned long ul_Ret = EM_INVAL;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}
	if( p_rdata == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */
	pHost = pSlot->pParentHost;
	if( pHost == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// PHY Register
	ul_hrs04 = HRS4_UIS_SET_READ_REQUEST | ul_uis_addr ;
	EM_WRITE_REG( pHost->pBaseAddress->HRS.HRS4.word, ul_hrs04);
	
	ul_Ret = em_waitForValue(
		&(pHost->pBaseAddress->HRS.HRS4.word),
		HRS4_UIS_SET_ACKNOWREDGE,
		1,
		EM_CONFIG_WAIT_VALUE_TIME );

	if (ul_Ret != EM_OK)
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	ul_hrs04 = EM_READ_REG( pHost->pBaseAddress->HRS.HRS4.word );
	*p_rdata = (unsigned char)((ul_hrs04 & HRS4_UIS_SET_READ_DATA_MASK) >> 16) ;

	// PHY Register UIS_RD, UIS_ACK Negate
	ul_hrs04 = ul_uis_addr ;
	EM_WRITE_REG( pHost->pBaseAddress->HRS.HRS4.word, ul_hrs04);

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief UHS-I PHY Register
 * @param[in] pSlot
 * @param[in] ul_uis_addr
 * @param[in] uc_wdata
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_DEV_NULL_POINTER
 */
/*****************************************************************************/
static unsigned long em_writePHYRegister(
	EM_SLOT* pSlot,
	unsigned long ul_uis_addr,
	unsigned char uc_wdata
){
	EM_HOST* pHost = NULL;
	unsigned long ul_hrs04 = 0;
	unsigned long ul_Ret = EM_INVAL;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}

	/**
	 * Process start
	 */
	pHost = pSlot->pParentHost;
	if( pHost == NULL )
	{
		DEBUG_TRACE_INFO_32( 0, EM_ERR_DEV_NULL_POINTER );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_DEV_NULL_POINTER;
	}

	// PHY Register
	ul_hrs04 = HRS4_UIS_SET_WRITE_REQUEST | ul_uis_addr | (uc_wdata << 8);
	EM_WRITE_REG( pHost->pBaseAddress->HRS.HRS4.word, ul_hrs04);
	
	ul_Ret = em_waitForValue(
		&(pHost->pBaseAddress->HRS.HRS4.word),
		HRS4_UIS_SET_ACKNOWREDGE,
		1,
		EM_CONFIG_WAIT_VALUE_TIME );

	if (ul_Ret != EM_OK)
	{
		DEBUG_TRACE_INFO_32( 0, ul_Ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	// PHY Register UIS_WR, UIS_ACK Negate
	ul_hrs04 = ul_uis_addr | (uc_wdata << 8);
	EM_WRITE_REG( pHost->pBaseAddress->HRS.HRS4.word, ul_hrs04);

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief training
 * @param[in] pSlot
 * @param[in] e_AccessMode
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 * @return EM_ERR_UNSUPPORTED_OPERATION
 */
/*****************************************************************************/
static unsigned long em_execTraining(
	EM_SLOT *pSlot,
	EM_ACCESS_MODE e_AccessMode
){
	unsigned char *p_read_buffer = NULL;
	unsigned long ul_srs11 = 0;
	unsigned long ul_sdcfs = 0;
	unsigned long ul_cnt = 0;
	unsigned long ul_cnt_ext = 0;
	unsigned long ul_ret = 0;
	unsigned long ul_ui_addr = 0;
	unsigned long ul_sdclk_delay = 0;
	unsigned char uc_center_value = 0;
	unsigned char uc_result[ EM_CONFIG_TRAINING_MAX_CNT ] = {0} ;
	unsigned char uc_pass_found = EM_DISABLE;

	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR ;
	}
	/**
	 * Process start
	 */
	
	// SDCLK delay adjustment
	while( ul_sdclk_delay < EM_MAX_PHY_SDCLK_DELAY )
	{
		// UHS-I PHY register
		ul_ret = em_writePHYRegister(
			pSlot,
			HRS4_UIS_ADDR_DLL_SDCLK_DELAY,
			(unsigned char)ul_sdclk_delay
		);
		if ( ul_ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_ret );
			DEBUG_TRACE_INFO_32( 4, ul_sdclk_delay );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}

		pSlot->uc_errorRecover = EM_ENABLE;
		// CMD Line Training(CMD16)
		ul_ret = em_CmdSetBlockLen(pSlot, EXTCSD_SIZE, EM_ENABLE);
		if ( ul_ret == EM_OK )
		{
			// success
			pSlot->uc_errorRecover = EM_DISABLE;
			break;
		}
		pSlot->uc_errorRecover = EM_DISABLE;

		// CMD line,DAT line Reset
		ul_ret = em_resetLines(
			pSlot,
			EM_ENABLE,
			EM_ENABLE
		);
		if ( ul_ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_ret );
			DEBUG_TRACE_INFO_32( 4, ul_sdclk_delay );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}

		// add the SDCLK delay
		ul_sdclk_delay += EM_CONFIG_PHY_SDCLK_DELAY_STEP;
	}

	// check training result
	if( ul_sdclk_delay >= EM_MAX_PHY_SDCLK_DELAY )
	{
		// fail
		return EM_INVAL;
	}

	// set block length
	ul_ret = em_CmdSetBlockLen(pSlot, EXTCSD_SIZE, EM_DISABLE );
	if ( ul_ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_ret );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// SRS11
	ul_srs11 = EM_READ_REG( pSlot->pRegSRS->SRS11.word );
	// SDCFSL/H
	ul_sdcfs = SRS11_GET_SDCFS( ul_srs11 );

	if( ul_sdcfs == 0 )
	{
		// 1/1 division(N=0) is abnormal
		DEBUG_TRACE_INFO_32( 0, ul_srs11 );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// training count : 2N * 2 = sdcfs * 4
	ul_sdcfs *= 4;
	
	// check the training count
	if( ul_sdcfs > EM_CONFIG_TRAINING_MAX_CNT )
	{
		DEBUG_TRACE_INFO_32( 0, ul_sdcfs );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION;
	}
	
	// UIS_ADDR
	switch(e_AccessMode)
	{
	case EM_ACCESS_MODE_MMC_LEGACY:
		ul_ui_addr = HRS4_UIS_ADDR_LEGACY_IN_DELAY;
		break;
	case EM_ACCESS_MODE_HS_SDR:
		ul_ui_addr = HRS4_UIS_ADDR_SDR_IN_DELAY;
		break;
	default:
		DEBUG_TRACE_INFO_32( 0, (unsigned long)e_AccessMode );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_UNSUPPORTED_OPERATION;
	}

	p_read_buffer = (unsigned char*)pSlot->ul_AuxBuff;
	// training start
	for(ul_cnt=0; ul_cnt<ul_sdcfs; ul_cnt++)
	{
		// UHS-I PHY register
		ul_ret = em_writePHYRegister(
			pSlot,
			ul_ui_addr,
			(unsigned char)ul_cnt
		);
		if ( ul_ret != EM_OK )
		{
			DEBUG_TRACE_INFO_32( 0, ul_ret );
			DEBUG_TRACE_INFO_32( 4, ul_cnt );
			DEBUG_TRACE_INFO_32( 8, ul_sdcfs );
			DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
			return EM_INVAL;
		}
		
		// Extends CSD
		ul_ret = em_CmdReadExCSD(pSlot, p_read_buffer);
		if ( ul_ret != EM_OK )
		{
			// fail
			uc_result[ul_cnt] = EM_TRAINING_FAIL ;
			// CMD line,DAT line Reset
			ul_ret = em_resetLines(
				pSlot,
				EM_ENABLE,
				EM_ENABLE
			);
			if ( ul_ret != EM_OK )
			{
				DEBUG_TRACE_INFO_32( 0, ul_ret );
				DEBUG_TRACE_INFO_32( 4, ul_cnt );
				DEBUG_TRACE_INFO_32( 8, ul_sdcfs );
				DEBUG_TRACE_INFO_32(12, ul_cnt_ext );
				DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
				return EM_INVAL;
			}
		} else {
			uc_result[ul_cnt] = EM_TRAINING_PASS ;
			for( ul_cnt_ext=0; ul_cnt_ext <(EXTCSD_SIZE / 4 ); ul_cnt_ext++)
			{
				if( pSlot->ul_AuxBuff[ul_cnt_ext] != pSlot->ul_ExtendsCSD[ul_cnt_ext] )
				{
					// fail
					uc_result[ul_cnt] = EM_TRAINING_FAIL ;
					// CMD line,DAT line Reset
					ul_ret = em_resetLines(
						pSlot,
						EM_ENABLE,
						EM_ENABLE
					);
					if ( ul_ret != EM_OK )
					{
						DEBUG_TRACE_INFO_32( 0, ul_ret );
						DEBUG_TRACE_INFO_32( 4, ul_cnt );
						DEBUG_TRACE_INFO_32( 8, ul_sdcfs );
						DEBUG_TRACE_INFO_32(12, ul_cnt_ext );
						DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
						return EM_INVAL;
					}
					break;
				}
			}
			if( uc_result[ul_cnt] == EM_TRAINING_PASS )
			{
				// pass found
				uc_pass_found = EM_ENABLE;
			} else {
				if( uc_pass_found == EM_ENABLE )
				{
					// Already detected the pass
					break;
				}
			}
		}
	}
	
	if( uc_pass_found != EM_ENABLE )
	{
		// all fail
		DEBUG_TRACE_INFO_32( 0, ul_sdcfs );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// center value calculation
	ul_ret = em_calcCenterValue(
		uc_result,
		ul_sdcfs,
		&uc_center_value
	);
	if ( ul_ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_ret );
		DEBUG_TRACE_INFO_32( 4, ul_ui_addr );
		for(ul_cnt=0; ul_cnt<ul_sdcfs; ul_cnt++){
			DEBUG_TRACE_INFO( ul_cnt+8, uc_result[ul_cnt] );
		}
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	// UHS-I PHY register
	ul_ret = em_writePHYRegister(
		pSlot,
		ul_ui_addr,
		uc_center_value
	);
	if ( ul_ret != EM_OK )
	{
		DEBUG_TRACE_INFO_32( 0, ul_ret );
		DEBUG_TRACE_INFO_32( 4, ul_ui_addr );
		DEBUG_TRACE_INFO( 8, uc_center_value );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	return EM_OK;
}

/*****************************************************************************/
/**
 * @brief set Extends CSD
 * @param[in] pSlot
 * @param[in] ul_byte
 * @param[in] uc_value
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
unsigned long em_setExtendsCSDInfo(
	EM_SLOT *pSlot,
	unsigned long ul_byte,
	unsigned char uc_value
){
	unsigned long ul_pos = 0;
	unsigned long ul_mask = 0xFF;
	unsigned long ul_shift = 0;
	/**
	 * parameter check
	 */
	if( pSlot == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	if( ul_byte >= EXTCSD_SIZE ) {
		DEBUG_TRACE_INFO_32( 0, ul_byte );
		DEBUG_TRACE_INFO( 4, uc_value );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}

	/**
	 * Process start
	 */

	ul_shift = ((ul_byte & 3) * 8 );
	ul_pos = (ul_byte / 4);
	ul_mask <<= ul_shift;
	
	pSlot->ul_ExtendsCSD[ ul_pos ] &= ~ul_mask;
	pSlot->ul_ExtendsCSD[ ul_pos ] |= (unsigned long)(uc_value << ul_shift);

	return EM_OK ;
}

/*****************************************************************************/
/**
 * @brief calculate center value
 * @param[in] p_uc_result
 * @param[in] ul_max
 * @param[out] p_uc_center_value
 * @return EM_OK
 * @return EM_INVAL
 * @return EM_ERR_PARAM_ERR
 */
/*****************************************************************************/
static unsigned long em_calcCenterValue(
	unsigned char *p_uc_result,
	unsigned long ul_max,
	unsigned char *p_uc_center_value
){
	unsigned long ul_cnt = 0;
	unsigned long ul_pass_min = EM_ALL_F_4_BYTE;
	unsigned long ul_pass_max = 0;
	unsigned long ul_calc_up = 1;

	/**
	 * parameter check
	 */
	if( p_uc_result == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	if( p_uc_center_value == NULL ) {
		DEBUG_TRACE_INFO_32( 0, EM_ERR_PARAM_ERR );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}
	// Training max count
	if( ul_max > EM_CONFIG_TRAINING_MAX_CNT ) {
		DEBUG_TRACE_INFO_32( 0, ul_max );
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_ERR_PARAM_ERR;
	}

	/**
	 * Process start
	 */
	*p_uc_center_value = 0;
	
	for(ul_cnt=0; ul_cnt<ul_max; ul_cnt++)
	{
		if( p_uc_result[ul_cnt] == EM_TRAINING_UNKNOWN )
		{
			// detect unknown (initial value)
			break;
		}

		// pass
		if( p_uc_result[ul_cnt] == EM_TRAINING_PASS )
		{
			if( ul_cnt < ul_pass_min )
			{
				// minimum count
				ul_pass_min = ul_cnt;
			}
			ul_pass_max = ul_cnt;
			continue;
		}

		// fail
		if( ul_pass_min != EM_ALL_F_4_BYTE )
		{
			// already detect the pass
			ul_calc_up = 0;
			break;
		}
	}

	// pass not detect
	if( ul_pass_min == EM_ALL_F_4_BYTE )
	{
		DEBUG_TRACE_INFO_32( 0, ul_max );
		for(ul_cnt=0; ul_cnt<ul_max; ul_cnt++){
			DEBUG_TRACE_INFO( ul_cnt+4, p_uc_result[ul_cnt] );
		}
		DEBUG_TRACE( FACT_EM_SWFAIL, &g_sTracedata );
		return EM_INVAL;
	}
	
	// calculate center value
	*p_uc_center_value = (unsigned char)(( ul_pass_min + ul_pass_max + ul_calc_up) / 2) ;
	
	return EM_OK;
}

